package bert;

import bert.collection.extensions.CX;
import bert.collection.extensions.EqComparer;
import bert.collection.extensions.Selector;
//import scala.collection.Traversable

public class Polygon {

    public final int Id;
    
	private final Vec2[] vertices;
	private final Side[] sides;
	public final AABB aabb;
	
	public Vec2[] GetVertices() { return this.vertices.clone(); }
	public Side[] GetSides() { return this.sides.clone(); }
  
	public Polygon(Vec2[] vs, int id)
	{
		assert vs != null;
		assert vs.length >= 3;
		
        this.Id = id;
		this.vertices = vs.clone();
	  
		this.sides = new Side[vs.length];
		
		for (int i = 0; i < vs.length; ++i)
		{
			Vec2 p0 = vs[i];
			Vec2 p1 = vs[(i+1) % vs.length];
			
			this.sides[i] = new Side(p0, p1);
		}
		
		Selector<Vec2, Integer> xSelector = new Selector<Vec2, Integer>() {
			@Override public Integer Select(Vec2 in) { return in.X; }
		}; 
		
		Selector<Vec2, Integer> ySelector = new Selector<Vec2, Integer>() {
			@Override public Integer Select(Vec2 in) { return in.Y; }
		}; 
		
		this.aabb = new AABB(
				CX.Min(vs, xSelector),
				CX.Min(vs, ySelector),
				CX.Max(vs, xSelector),
				CX.Max(vs, ySelector)
				);
  }
  
	public boolean contains(Vec2 p)
	{
		boolean c = false;
		
		for (int i = 0; i < this.vertices.length; ++i)
		{
			Vec2 p0 = this.vertices[i];
			Vec2 p1 = this.vertices[(i+1) % this.vertices.length];
			
			if ( ((p1.Y > p.Y) != (p0.Y > p.Y)) && 
			           (p.X < (p0.X - p1.X) * (float)(p.Y - p1.Y) / (p0.Y - p1.Y) + p1.X))
				c = !c;
		}
		
		return c;
  	}
  
	
    public boolean isEquivalentTo(Polygon other) {
    	
    	EqComparer<Vec2> comparer = new EqComparer<Vec2>() {
			@Override public boolean AreEquivalent(Vec2 obj1, Vec2 obj2) {
				return obj1.isEquivalentTo(obj2);
			}
		};
    	
    	return CX.Correspond(this.vertices, other.vertices, comparer);
    }
    
  	public boolean isAdjacentTo(Polygon other) 
  	{
  		return adjacentSideWith(other) != null;
  	}
  
  	public Side adjacentSideWith(Polygon other) 
  	{
  		if (!other.aabb.expanded(3).intersects(this.aabb))
  			return null;
  		
  		for (Side thisSide : this.sides)
  		{
  			for (Side otherSide : other.sides)
  			{
  				if (thisSide.isEquivalentTo(otherSide))
  					return thisSide;
  			}
  		}
  		
  		return null;
  	}
  	
  	public Vec2 ComputeCenter() {
  		
  		int x = this.vertices[0].X;
  		int y = this.vertices[0].Y;
  		
  		for ( int i = 1 ; i < this.vertices.length; ++i)
  		{
  			x += this.vertices[i].X;
  			y += this.vertices[i].Y;
  		}
  		
  		return new Vec2(x / this.vertices.length, y / this.vertices.length);
  	}
  	
	public int GetVertexCount() {
		return this.vertices.length;
	}
}